(* Tests for the Cyclesim random initial value config *)

open! Import
open Hardcaml
open Hardcaml_waveterm_cyclesim

module I = struct
  type 'a t =
    { clock : 'a
    ; clear : 'a
    }
  [@@deriving hardcaml]
end

module O = struct
  (* We make these signals wide and avoid them being a multiple of 8 so that we see the
     whole bit range is initialized randomly. *)
  type 'a t =
    { register : 'a [@bits 171]
    ; memory_read : 'a [@bits 191]
    }
  [@@deriving hardcaml]
end

let create ?(collision_mode = Ram.Collision_mode.Read_before_write) { I.clock; clear } =
  let open Signal in
  let reg_spec_with_clear = Reg_spec.create ~clock ~clear () in
  let ram_size = 4 in
  let address_bits = address_bits_for ram_size in
  let address = reg_fb reg_spec_with_clear ~width:address_bits ~f:(fun d -> d +:. 1) in
  let ram =
    Ram.create
      ~collision_mode
      ~size:ram_size
      ~name:"test"
      ~read_ports:
        [| { Read_port.read_clock = clock; read_address = address; read_enable = vdd } |]
      ~write_ports:
        [| { Write_port.write_clock = clock
           ; write_address = zero address_bits
           ; write_enable = gnd
           ; write_data = zero 191
           }
        |]
      ()
  in
  let reg = reg reg_spec_with_clear (zero 171) in
  { O.register = reg; memory_read = ram.(0) }
;;

let run (waves, sim) =
  Cyclesim.cycle sim;
  Cyclesim.cycle sim;
  Cyclesim.cycle sim;
  Waveform.print ~display_width:160 ~wave_width:25 waves
;;

let%expect_test "initialized to zero" =
  let module Sim = Cyclesim.With_interface (I) (O) in
  Sim.create create |> Waveform.create |> run;
  [%expect
    {|
    ┌Signals───────────┐┌Waves─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │clock             ││┌─────────────────────────┐                         ┌─────────────────────────┐                         ┌─────────────────────────┐       │
    │                  ││                          └─────────────────────────┘                         └─────────────────────────┘                         └───────│
    │clear             ││                                                                                                                                          │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │memory_read       ││ 000000000000000000000000000000000000000000000000                                                                                         │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │register          ││ 0000000000000000000000000000000000000000000                                                                                              │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    └──────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    |}]
;;

let%expect_test "pseudorandomly initialized registers" =
  (* Note that there is some interesting behaviour here for the memory read - on the first
     cycle it is random, even though we are not randomizing memories. This is because they
     are built from an async memory blob, followed by a register. In [Read_before_write]
     the register is on the output data port. *)
  let module Sim = Cyclesim.With_interface (I) (O) in
  Sim.create
    ~config:
      Cyclesim.Config.(
        add_random_initialization default Random_initializer.(create randomize_regs))
    create
  |> Waveform.create
  |> run;
  [%expect
    {|
    ┌Signals───────────┐┌Waves─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │clock             ││┌─────────────────────────┐                         ┌─────────────────────────┐                         ┌─────────────────────────┐       │
    │                  ││                          └─────────────────────────┘                         └─────────────────────────┘                         └───────│
    │clear             ││                                                                                                                                          │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────│
    │memory_read       ││ 23828C1DDF82F3502295674CA6A1E8876AE4C48206C1097E   │000000000000000000000000000000000000000000000000                                     │
    │                  ││────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────│
    │register          ││ 15598B6B983D30B054265133DD79CA066F1A4AB2EEA        │0000000000000000000000000000000000000000000                                          │
    │                  ││────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────│
    └──────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    |}];
  (* However, in [Write_before_read] mode it is the address that is randomized, so we read
     out [0] at the start. *)
  Sim.create
    ~config:
      Cyclesim.Config.(
        add_random_initialization default Random_initializer.(create randomize_regs))
    (create ~collision_mode:Write_before_read)
  |> Waveform.create
  |> run;
  [%expect
    {|
    ┌Signals───────────┐┌Waves─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │clock             ││┌─────────────────────────┐                         ┌─────────────────────────┐                         ┌─────────────────────────┐       │
    │                  ││                          └─────────────────────────┘                         └─────────────────────────┘                         └───────│
    │clear             ││                                                                                                                                          │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │memory_read       ││ 000000000000000000000000000000000000000000000000                                                                                         │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────│
    │register          ││ 15598B6B983D30B054265133DD79CA066F1A4AB2EEA        │0000000000000000000000000000000000000000000                                          │
    │                  ││────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────│
    └──────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    |}]
;;

let%expect_test "pseudorandomly initialized memories" =
  let module Sim = Cyclesim.With_interface (I) (O) in
  Sim.create
    ~config:
      Cyclesim.Config.(
        add_random_initialization
          Cyclesim.Config.default
          Random_initializer.(create randomize_memories))
    create
  |> Waveform.create
  |> run;
  [%expect
    {|
    ┌Signals───────────┐┌Waves─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │clock             ││┌─────────────────────────┐                         ┌─────────────────────────┐                         ┌─────────────────────────┐       │
    │                  ││                          └─────────────────────────┘                         └─────────────────────────┘                         └───────│
    │clear             ││                                                                                                                                          │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬───────────────────────────────────────────────────┬─────────────────────────────────│
    │memory_read       ││ 000000000000000000000000000000000000000000000000   │57F1515598B6B983D30B054265133DD79CA066F1A4AB2EEA   │23828C1DDF82F3502295674CA6A1E8876│
    │                  ││────────────────────────────────────────────────────┴───────────────────────────────────────────────────┴─────────────────────────────────│
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │register          ││ 0000000000000000000000000000000000000000000                                                                                              │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    └──────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    |}]
;;

let%expect_test "pseudorandomly initialized state" =
  let module Sim = Cyclesim.With_interface (I) (O) in
  Sim.create
    ~config:
      Cyclesim.Config.(
        add_random_initialization default Random_initializer.(create (Fn.const true)))
    create
  |> Waveform.create
  |> run;
  [%expect
    {|
    ┌Signals───────────┐┌Waves─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │clock             ││┌─────────────────────────┐                         ┌─────────────────────────┐                         ┌─────────────────────────┐       │
    │                  ││                          └─────────────────────────┘                         └─────────────────────────┘                         └───────│
    │clear             ││                                                                                                                                          │
    │                  ││──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬───────────────────────────────────────────────────┬─────────────────────────────────│
    │memory_read       ││ 23828C1DDF82F3502295674CA6A1E8876AE4C48206C1097E   │60116B8A19F1278A42A87508EB5887FE1B6E65B65E56BCBC   │0048B557EB69B0FAF970AA7E4D42EF5C8│
    │                  ││────────────────────────────────────────────────────┴───────────────────────────────────────────────────┴─────────────────────────────────│
    │                  ││────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────│
    │register          ││ 15598B6B983D30B054265133DD79CA066F1A4AB2EEA        │0000000000000000000000000000000000000000000                                          │
    │                  ││────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────│
    └──────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    |}]
;;
